package com.dnjn.happiness.auth.service;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.aliyun.dingtalkoauth2_1_0.Client;
import com.aliyun.dingtalkoauth2_1_0.models.GetAccessTokenRequest;
import com.aliyun.dingtalkoauth2_1_0.models.GetAccessTokenResponse;
import com.aliyun.dingtalkoauth2_1_0.models.GetCorpAccessTokenRequest;
import com.aliyun.dingtalkoauth2_1_0.models.GetCorpAccessTokenResponse;
import com.aliyun.tea.TeaException;
import com.aliyun.teaopenapi.models.Config;
import com.aliyun.teautil.Common;
import com.dingtalk.oapi.lib.aes.DingTalkEncryptor;
import com.dnjn.happiness.auth.config.DingTalkConfig;
import com.dnjn.happiness.common.core.utils.StringUtils;
import com.dnjn.happiness.common.redis.service.RedisService;
import com.dnjn.happiness.common.security.service.TokenService;
import com.dnjn.happiness.system.api.RemoteUserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;

import java.util.Map;

@Slf4j
@Service
public class DingTalkServiceImpl implements IDingTalkService {
    /**
     * 创建应用，验证回调URL创建有效事件（第一次保存回调URL之前）
     */
    private static final String EVENT_CHECK_CREATE_SUITE_URL = "check_create_suite_url";

    /**
     * 创建应用，验证回调URL变更有效事件（第一次保存回调URL之后）
     */
    private static final String EVENT_CHECK_UPADTE_SUITE_URL = "check_update_suite_url";

    /**
     * suite_ticket推送事件
     */
    private static final String EVENT_SUITE_TICKET = "suite_ticket";

    /**
     * 企业授权开通应用事件
     */
    private static final String EVENT_TMP_AUTH_CODE = "tmp_auth_code";

    @Autowired
    private DingTalkConfig appConfig;

    @Autowired
    private RedisService redisCache;

    @Autowired
    private RemoteUserService userService;

    @Autowired
    private TokenService tokenService;


    /**
     * 使用 Token 初始化账号Client
     *
     * @return Client
     * @throws Exception
     */

    public static Client createClient() throws Exception {
        Config config = new Config();
        config.protocol = "https";
        config.regionId = "central";
        return new Client(config);
    }

    public String getAccessToken(String suiteKey, String suiteSecret, String authCorpId, String suiteTicket) {
        String access_token = null;
        try {
            Client client = DingTalkServiceImpl.createClient();
            suiteTicket = redisCache.getCacheObject("suiteTicket");
            GetCorpAccessTokenRequest getCorpAccessTokenRequest = new GetCorpAccessTokenRequest()
                    .setSuiteKey(suiteKey)
                    .setSuiteSecret(suiteSecret)
                    .setAuthCorpId(authCorpId)
                    .setSuiteTicket(suiteTicket);
//                    .setSuiteKey("suitep1f5lzyglm7fryxxxx")
//                    .setSuiteSecret("_FP5PpZF3irDKjxxx")
//                    .setAuthCorpId("ding123456")
//                    .setSuiteTicket("1f5lzyglm7fryxxxx");

            GetCorpAccessTokenResponse accessToken = client.getCorpAccessToken(getCorpAccessTokenRequest);
            access_token = accessToken.getBody().getAccessToken();
            Long expireIn = accessToken.getBody().getExpireIn();

        } catch (TeaException err) {
            if (!Common.empty(err.code) && !Common.empty(err.message)) {
                // err 中含有 code 和 message 属性，可帮助开发定位问题
                err.printStackTrace();
                log.error("获取token失败" + err.code + err.message);
            }

        } catch (Exception _err) {
            TeaException err = new TeaException(_err.getMessage(), _err);
            if (!Common.empty(err.code) && !Common.empty(err.message)) {
                // err 中含有 code 和 message 属性，可帮助开发定位问题
                err.printStackTrace();
                log.error(err.code + err.message);
            }

        }
        return access_token;
    }


    public String getAccessToken(String appKey, String appSecret) {
        String access_token = null;
        try {
            Client client = DingTalkServiceImpl.createClient();
            GetAccessTokenRequest getAccessTokenRequest = new GetAccessTokenRequest()
                    .setAppKey(appKey)
                    .setAppSecret(appSecret);
            GetAccessTokenResponse accessToken = client.getAccessToken(getAccessTokenRequest);
            access_token = accessToken.getBody().getAccessToken();
            Long expireIn = accessToken.getBody().getExpireIn();

        } catch (TeaException err) {
            if (!Common.empty(err.code) && !Common.empty(err.message)) {
                // err 中含有 code 和 message 属性，可帮助开发定位问题
                err.printStackTrace();
                log.error(err.code + err.message);
            }

        } catch (Exception _err) {
            TeaException err = new TeaException(_err.getMessage(), _err);
            if (!Common.empty(err.code) && !Common.empty(err.message)) {
                // err 中含有 code 和 message 属性，可帮助开发定位问题
                err.printStackTrace();
                log.error(err.code + err.message);
            }

        }
        return access_token;
    }


    @Override
    public Map<String, String> handleDingTalkCallback(String signature, Long timestamp, String nonce, JSONObject body)  {
        String params = "signature:" + signature + " timestamp:" + timestamp + " nonce:" + nonce + " body:" + body;
        try {
            log.info("begin callback:" + params);
            DingTalkEncryptor dingTalkEncryptor = new DingTalkEncryptor(appConfig.getToken(), appConfig.getEncodingAesKey(), appConfig.getClientId());

            // 从post请求的body中获取回调信息的加密数据进行解密处理
            String encrypt = body.getString("encrypt");
            String plainText = dingTalkEncryptor.getDecryptMsg(signature, timestamp.toString(), nonce, encrypt);
            JSONObject callBackContent = JSON.parseObject(plainText);

            // 根据回调事件类型做不同的业务处理
            String eventType = callBackContent.getString("EventType");
            if (EVENT_CHECK_CREATE_SUITE_URL.equals(eventType)) {
                log.info("验证新创建的回调URL有效性: " + plainText);
            } else if (EVENT_CHECK_UPADTE_SUITE_URL.equals(eventType)) {
                log.info("验证更新回调URL有效性: " + plainText);
            } else if (EVENT_SUITE_TICKET.equals(eventType)) {
                // suite_ticket用于用签名形式生成accessToken(访问钉钉服务端的凭证)，需要保存到应用的db。plainText
                // 钉钉会定期向本callback url推送suite_ticket新值用以提升安全性。
                // 应用在获取到新的时值时，保存db成功后，返回给钉钉success加密串（如本demo的return）
                log.info("应用suite_ticket数据推送: " + plainText);
            } else if (EVENT_TMP_AUTH_CODE.equals(eventType)) {
                // 本事件应用应该异步进行授权开通企业的初始化，目的是尽最大努力快速返回给钉钉服务端。用以提升企业管理员开通应用体验
                // 即使本接口没有收到数据或者收到事件后处理初始化失败都可以后续再用户试用应用时从前端获取到corpId并拉取授权企业信息，进而初始化开通及企业。
                log.info("企业授权开通应用事件: " + plainText);
            } else {
                // 其他类型事件处理
            }
            String bizData = callBackContent.getString("bizData");
            if (!StringUtils.isEmpty(bizData)) {
                String suiteTicket = JSON.parseObject(JSON.parseObject(JSONArray.parse(bizData).get(0).toString()).get("biz_data").toString()).getString("suiteTicket");
                Object redisCacheCacheObject = redisCache.getCacheObject("suiteTicket");
                if (ObjectUtils.isEmpty(redisCacheCacheObject)) {
                    redisCache.setCacheObject("suiteTicket", suiteTicket);
                } else {
                    redisCache.deleteObject("suiteTicket");
                    redisCache.setCacheObject("suiteTicket", suiteTicket);
                }
            }
            // 返回success的加密信息表示回调处理成功
            return dingTalkEncryptor.getEncryptedMap("success", timestamp, nonce);
        } catch (Exception e) {
            //失败的情况，应用的开发者应该通过告警感知，并干预修复
            log.error("process callback fail." + params, e);
            //todo
            return Map.of("code","fail");
        }
    }


    /**
     * 根据authCode获取用户ID
     *
     * @param authCode
     * @param
     * @return
     */

   /* public String getUserInfo(String authCode) {

        //1. 获取token
        String accessToken = getAccessToken(appConfig.getClientId(), appConfig.getClientSecret(), appConfig.getCorpId(), appConfig.getSuiteTicket());

        // 获取用户信息
        String token = "";
        DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/user/getuserinfo");
        OapiUserGetuserinfoRequest request = new OapiUserGetuserinfoRequest();
        request.setCode(authCode);
        request.setHttpMethod("GET");
        OapiUserGetuserinfoResponse response;
        try {
            response = client.execute(request, accessToken);
        } catch (ApiException e) {
            e.printStackTrace();
            log.error("获取钉钉用户信息失败");
            return token;
        }
        // 查询得到当前用户的userId
        // 获得到userId之后应用应该处理应用自身的登录会话管理（session）,避免后续的业务交互（前端到应用服务端）每次都要重新获取用户身份，提升用户体验
        String userId = response.getUserid();
        if (!StringUtils.isEmpty(userId)) {
            //通过用户userId（工号）查询用户
            SysUser user = userService.getUserByEmpno(userId);
            if (null != user) {
                LoginUser loginUser = new LoginUser();
                loginUser.setUser(user);
                loginUser.setUserId(user.getUserId());
                loginUser.setDeptId(user.getDeptId());
                // 生成token
                token = tokenService.createToken(loginUser);
            }
        }

        // 3. 根据用户ID获取用户详情

        try {
            DingTalkClient client1 = new DefaultDingTalkClient("https://oapi.dingtalk.com/user/get");
            OapiUserGetRequest req = new OapiUserGetRequest();
            req.setUserid(userId);
            req.setHttpMethod("GET");
            OapiUserGetResponse rsp = client1.execute(req, accessToken);
            String jobnumber = rsp.getJobnumber();
        } catch (ApiException e) {
            e.printStackTrace();
        }


        return token;
    }
*/

/**
 * 根据authCode获取用户ID
 *
 * @param authCode
 * @param accessToken
 * @return
 *//*

    private String getUserId(String authCode, String accessToken) {
        DingTalkClient client = new DefaultDingTalkClient(Constants.GET_USER_INFO_URL);
        OapiV2UserGetuserinfoRequest req = new OapiV2UserGetuserinfoRequest();
        req.setCode(authCode);
        OapiV2UserGetuserinfoResponse oapiV2UserGetuserinfoResponse;
        try {
            oapiV2UserGetuserinfoResponse = client.execute(req, accessToken);
            if (oapiV2UserGetuserinfoResponse.isSuccess()) {
                OapiV2UserGetuserinfoResponse.UserGetByCodeResponse userGetByCodeResponse = oapiV2UserGetuserinfoResponse.getResult();
                return userGetByCodeResponse.getUserid();
            } else {
                throw new Exception(oapiV2UserGetuserinfoResponse.getErrorCode()+oapiV2UserGetuserinfoResponse.getErrmsg());
            }

        } catch (Exception e) {
            // 需要自己处理异常
            e.printStackTrace();
           log.error(e.getMessage());
        }
        return null;
    }


/**
     * 根据用户ID获取用户详情
     *
     * @param userId
     * @param accessToken
     * @return
     */

    /*private OapiV2UserGetResponse.UserGetResponse getOapiV2UserGetResponseByUserId(String userId, String accessToken) {
        DingTalkClient client = new DefaultDingTalkClient(Constants.USER_GET_URL);
        OapiV2UserGetRequest req = new OapiV2UserGetRequest();
        req.setUserid(userId);
        req.setLanguage("zh_CN");
        try {
            OapiV2UserGetResponse oapiV2UserGetResponse = client.execute(req, accessToken);
            if (oapiV2UserGetResponse.isSuccess()) {
                return oapiV2UserGetResponse.getResult();
            } else {
                throw new Exception(oapiV2UserGetResponse.getErrorCode() + oapiV2UserGetResponse.getErrmsg());
            }
        } catch (Exception e) {
            // 需要自己处理异常
            e.printStackTrace();
            log.error(e.getMessage());
        }
        return null;
    } */
}

